﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using MTS;
using NDesk.Options;

namespace MTSCLI
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.CursorVisible = false;
            string n = Environment.NewLine;

            Console.Title = "Minecraft Topographical Survey CLI v" + Assembly.GetExecutingAssembly().GetName().Version;

            Console.WriteLine(Console.Title);
            Console.WriteLine("Download source at http://minecraft.codeplex.com/");
            Console.WriteLine();

            if (!File.Exists("blockcolors.xml") || !File.Exists("mts.dll"))
            {
                WriteLineColored("Required files are missing from your MTS installation.", ConsoleColor.DarkRed, ConsoleColor.White);
                Console.WriteLine("Please extract all the files from the MTS download to the same location.");
                Console.WriteLine("MTS cannot continue without all files present, and will now quit.");
                Console.WriteLine();

                goto End;
            }

            bool Help = false;

            string OutDir = String.Empty;
            string CacheDir = String.Empty;

            List<RenderSettings> RenderTasks = new List<RenderSettings>();

            BlockColors BlockTool = new BlockColors();

            var p = new OptionSet() {
                { "?|help", "Show this message and exit.", 
                    v => Help = v != null },

                //Rendering modes
                { "t|terrain", "Render terrain map.",
                    v => { if (v != null) { RenderTasks.Add(new RenderSettings { TileType = ImageType.TERRAIN }); }}},
                { "o|oblique=", "Render oblique map. You must specify a lighting level between 1 and 16.",
                    (int v) => {
                        if (v >= 1 && v <= 16)
                            RenderTasks.Add(new RenderSettings { TileType = ImageType.OBLIQUE, LightingLevel = (byte)v });
                        else {
                            WriteLineColored("Not rendering oblique mode with option '" + v + "':" + n + 
                                "Oblique brightness must be between 1 and 16.", ConsoleColor.DarkRed, ConsoleColor.White);
                            Console.WriteLine();
                        }
                    }},
                { "c|cave", "Render cave map.",
                    v => { if (v != null) { RenderTasks.Add(new RenderSettings { TileType = ImageType.CAVEMAP }); }}},
                { "h|height", "Render heightmap.",
                    v => { if (v != null) { RenderTasks.Add(new RenderSettings { TileType = ImageType.HEIGHTMAP }); }}},
                { "s|spectro=", "Render spectrograph. You must specify a block type number.",
                    (int v) => {
                        if (BlockTool.Blocks.ContainsKey(v))
                            RenderTasks.Add(new RenderSettings { TileType = ImageType.SPECTROGRAPH, SpectralBlock = (byte)v });
                        else {
                            WriteLineColored("Not rendering spectrograph mode with option '" + v + "':" + n + 
                                "Block '" + v + "' isn't present in blockcolors.xml. Are you sure it exists?", ConsoleColor.DarkRed, ConsoleColor.White);
                            Console.WriteLine();
                        }
                    }},

                //Extra options
                { "out=", "Set the output directory. By default, the directory mts is running in will be used.",
                    v => { if (v != null) { OutDir = v; } }},

                { "cache=", "Set the cache directory. By default, a folder in AppData is used.",
                    v => { 
                        if (v != null) { 
                            if (Directory.Exists(v)) {
                                CacheDir = v;
                            } 
                            else 
                            {
                                try {
                                    Directory.CreateDirectory(v);
                                    CacheDir = v;
                                } catch (Exception ex) {
                                    WriteLineColored("Cache folder '" + v + "' doesn't exist and couldn't be created." + n + "Using default cache.", ConsoleColor.DarkRed, ConsoleColor.White);
                                }
                            }
                        }
                    }},
            };

            List<string> extra;
            try
            {
                extra = p.Parse(args);
            }
            catch (OptionException e)
            {
                WriteLineColored(e.Message, ConsoleColor.DarkRed, ConsoleColor.White);
                Console.WriteLine("Try 'mts-cli --help' for more information.");
                goto End;
            }

            if (Help)
            {
                p.WriteOptionDescriptions(Console.Out);
                Console.WriteLine(n + "  ...followed by any number of \"quoted\" level.dat paths.");
            }
            else
            {
                //Check if an output dir was specified and if it exists
                if (!string.IsNullOrEmpty(OutDir))
                {
                    if (!Directory.Exists(OutDir))
                    {
                        try
                        {
                            Directory.CreateDirectory(OutDir);
                        }
                        catch (Exception ex)
                        {
                            WriteLineColored("Output directory \"" + OutDir + "\" doesn't exist, and couldn't be created." + n + 
                                "Using local path instead.", ConsoleColor.DarkRed, ConsoleColor.White);
                        }
                    }
                }
                if (string.IsNullOrEmpty(OutDir))
                {
                    OutDir = Assembly.GetExecutingAssembly().Location;
                    OutDir = OutDir.Substring(0, OutDir.LastIndexOf('\\'));
                }

                //Make sure all the remaining arguments are worlds; dump any that aren't or don't exist
                List<string> Worlds = new List<string>();
                foreach (string s in extra)
                {
                    if (s.EndsWith("level.dat"))
                    {
                        if (File.Exists(s))
                            Worlds.Add(s.Substring(0, s.LastIndexOf("level.dat") - 1));
                        else
                            WriteLineColored("Not rendering \"" + s + "\":" + n + "File doesn't exist!", ConsoleColor.DarkRed, ConsoleColor.White);
                    }
                    else
                    {
                        WriteLineColored("Not rendering \"" + s + "\":" + n + "Path must be a Minecraft level.dat file.", ConsoleColor.DarkRed, ConsoleColor.White);
                    }
                }
                Console.WriteLine();

                if (Worlds.Count == 0)
                {
                    WriteLineColored("No worlds specified. Try 'mts-cli --help' for more information.", ConsoleColor.DarkRed, ConsoleColor.White);
                    goto End;
                }
                if (RenderTasks.Count == 0)
                {
                    WriteLineColored("No map types specified. Try 'mts-cli --help' for more information.", ConsoleColor.DarkRed, ConsoleColor.White);
                    goto End;
                }

                //Count the tasks and show the number
                WriteLineColored("Rendering " + Worlds.Count + (Worlds.Count > 1 ? " worlds, " : " world, ") +
                    RenderTasks.Count + (RenderTasks.Count > 1 ? " maps " : " map ") +  "each - " + 
                    (Worlds.Count * RenderTasks.Count) + ((Worlds.Count * RenderTasks.Count) > 1 ? " tasks" : " task") + " total.",
                    ConsoleColor.DarkMagenta, ConsoleColor.White);
                Console.WriteLine();

                //Go hog wild
                Surveyor S = new Surveyor();

                AutoResetEvent ResetEvent = new AutoResetEvent(false);

                S.WorkComplete += (Message) =>
                {
                    WriteLineColored(Message, ConsoleColor.DarkMagenta, ConsoleColor.White);
                    ResetEvent.Set();
                    return;
                };

                if (!String.IsNullOrEmpty(CacheDir))
                    S.Render(Worlds, RenderTasks, OutDir, CacheDir);
                else
                    S.Render(Worlds, RenderTasks, OutDir);

                ResetEvent.WaitOne();
            }

        End:

#if DEBUG
            Console.WriteLine("DEBUG: Press any key to exit.");
            Console.Read();
#endif

            return;
        }

        public static void WriteColored(string Text, ConsoleColor Background, ConsoleColor Foreground)
        {
            ConsoleColor LastBG = Console.BackgroundColor;
            ConsoleColor LastFG = Console.ForegroundColor;
            Console.BackgroundColor = Background;
            Console.ForegroundColor = Foreground;
            Console.Write(Text);
            Console.BackgroundColor = LastBG;
            Console.ForegroundColor = LastFG;
        }

        public static void WriteLineColored(string Text, ConsoleColor Background, ConsoleColor Foreground)
        {
            WriteColored(Text + Environment.NewLine, Background, Foreground);
        }
    }
}
